/*********************************************************************************
 *      Copyright:  (C) 2024 LingYun<iot25@lingyun>
 *                  All rights reserved.
 *
 *       Filename:  server_main.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(08/12/24)
 *         Author:  LingYun <iot25@lingyun>
 *      ChangeLog:  1, Release initial version on "08/12/24 14:44:52"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sqlite3.h>
#include <getopt.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>

#include "get_time.h"
#include "socket_cnn.h"
#include "sqlite_database.h"
#include "log_system.h"

#define MAX_EVENTS 1024

struct option  opts[]=
{
    {"port", required_argument, NULL, 'p'},
    {"debug", no_argument, NULL, 'd'},
    {"help", no_argument, NULL, 'h'},
    {NULL, 0, NULL, 0}
};

void print_usage(char *progname)
{
	printf("%s usage:\n",progname);
    
	printf("-p(--port):sepcify server listen port.\n");
    printf("-d(--debug):  specify program running in debug mode\n");
    printf("-h(--help):   print this help information\n");

    printf("\nExample: %s -p 8899 -d\n", progname);

	return ;
}


int main(int argc,char **argv)
{
	int						port = 0;
	int 					ch;
	char					*progname = argv[0];

	int						server_fd = -1;
	int						client_fd = -1;
	
	struct sockaddr_in 		cli_addr;
	socklen_t				cliaddr_len = sizeof(cliaddr_len);
	char					buf[1024];
	char					time_buf[80];

	sqlite3					*db;
	int						rc;

	int						epoll_fd;
	struct epoll_event 		event;
    struct epoll_event 		events[MAX_EVENTS];

	//初始化日志系统，日志级别为DEBUG，输出到控制台和文件
    log_init("server_program.log", LOG_DEBUG, 1);
	
	//解析命令行参数
	while( (ch = getopt_long(argc, argv, "p:db", opts, NULL)) != -1)
	{
		switch( ch )
		{
			case 'p':
				port = atoi(optarg);
				break;

			case 'h':
				print_usage(progname);
				log_write(LOG_INFO, __FILE__, __func__, __LINE__, "显示帮助信息");
				return EXIT_SUCCESS;

			default:
				print_usage(progname);
				log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "命令行参数错误");
				return EXIT_FAILURE;
		}
	}

	//检查命令行是否输入正确
	if( !port )
	{
		print_usage(progname);
		log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "缺少必要参数");
		return EXIT_FAILURE;
	}
	log_write(LOG_INFO, __FILE__, __func__, __LINE__, "指定端口号为：%d.\n", port);
	
	//打开数据库
	rc = sqlite3_open("serverdata.db",&db);
	if( rc < 0)
	{
		log_write(LOG_ERROR,__FILE__,__func__,__LINE__,"无法打开数据库:%s",sqlite3_errmsg(db));
		return rc;
	}
	
	//创建数据库
	rc = create_db_and_table(db);
	if( rc != SQLITE_OK )
	{
		 sqlite3_close(db);
		 log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "无法打开数据库: %s", sqlite3_errmsg(db));
		 return rc;
	}
	
	//创建服务器端套接字并进行绑定
	server_fd = server_connect_to_client(port);

	//创建epoll实例
	epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) 
	{
		log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "创建 epoll 实例失败");
        close_socket(server_fd);
        return EXIT_FAILURE;
    }
	
	// 将服务器套接字加入 epoll 监控
    event.events = EPOLLIN;
    event.data.fd = server_fd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) 
	{
		log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "将服务器套接字加入 epoll 失败");
        close_socket(server_fd);
        close(epoll_fd);
        return EXIT_FAILURE;
    }
	log_write(LOG_INFO, __FILE__, __func__, __LINE__, "服务器启动，开始监听端口 %d", port);

	//进入epoll循环，处理客户端请求
	while(1)
	{
		 int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);  // 等待事件发生
        if (n == -1) 
		{
            log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "epoll_wait 失败");
            break;
        }

		for (int i = 0; i < n; i++)
		{
			if (events[i].data.fd == server_fd) 
			{
				//服务器套接字有新连接
				client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cliaddr_len);
                if (client_fd == -1) 
				{
                    log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "接收客户端连接失败");
                    continue;
                }

				//将新客户端套接字加入 epoll 监控
                event.events = EPOLLIN | EPOLLET;  // 边缘触发模式
                event.data.fd = client_fd;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) 
				{
					log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "将客户端套接字加入 epoll 失败");
                    close_socket(client_fd);
                    continue;
                }

				log_write(LOG_INFO, __FILE__, __func__, __LINE__, "接受新的客户端[%s:%d]，套接字 [%d]",\
                          inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), client_fd);
			}

			else
			{
				//客户端套接字有数据可读
				client_fd = events[i].data.fd;
                memset(buf, 0, sizeof(buf));
            	int bytes_read = read_data(client_fd, buf, sizeof(buf));
				
				if(bytes_read <= 0) 
				{
					//客户端断开连接或出错，关闭连接
                    log_write(LOG_WARN, __FILE__, __func__, __LINE__, "客户端[%d]断开连接", client_fd);
                    close_socket(client_fd);
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL);
                }
				
				else
				{
					//处理收到的数据
                    buf[bytes_read] = '\0';
                    get_time(time_buf, sizeof(time_buf));
                    log_write(LOG_INFO, __FILE__, __func__, __LINE__, "从客户端client_fd[%d]接收数据: %s, 获取的时间是: %s",client_fd,buf, time_buf);
					
					//插入数据到数据库
                    if(strlen(buf) > 0) 
					{
						insert_data(db, buf);
						log_write(LOG_INFO, __FILE__, __func__, __LINE__, "插入数据%s到数据库成功",buf);
					} 
					else 
					{
                        log_write(LOG_ERROR, __FILE__, __func__, __LINE__, "收到空数据，跳过插入操作");
                    }
				}
			}
		}
	}

	//关闭 epoll 和套接字
    close(epoll_fd);
    close_socket(server_fd);
    log_close();

	return 0;
}
